package com.sping_boot3.spirng_boot3.controller;

import com.sping_boot3.spirng_boot3.entity.Singleton;
import com.sping_boot3.spirng_boot3.juc.*;
import jakarta.annotation.Resource;
import lombok.Data;
import lombok.SneakyThrows;
import lombok.Synchronized;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.Duration;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.locks.ReentrantLock;

@RestController
@RequestMapping("demo")
public class DemoController {

    @Resource
    StringRedisTemplate stringRedisTemplate;

    @Resource()
    ReentrantLock reentrantLock;
    @Resource
    Alternate_synchronized alternate_synchronized;
    @Resource
    Alternate_Lock alternate_lock;
    @Resource
    线程定制化化通信 线程定制化化通信;
    /**
     * 公平锁
     */
    ReentrantLock fairLock = new ReentrantLock(true);
    ArrayList<String> list = new ArrayList();
    Vector<String> vector = new Vector<>();

    List<String> synchronizedList = Collections.synchronizedList(new ArrayList<>());

    CopyOnWriteArrayList<String> copyOnWriteArrayList = new CopyOnWriteArrayList<>();

    Set<String> set = new HashSet<>();

    CopyOnWriteArraySet<String> copyOnWriteArraySet = new CopyOnWriteArraySet<>();

    ConcurrentHashMap<String, String> concurrentHashMap = new ConcurrentHashMap<>();

    @GetMapping("grab/tickets/{id}")
    public String grabTickets(@PathVariable("id") Long id) {
        //Synchronized实现抢票
//         SynchronizedUse(id);
        //如果锁放在线程外面会导致提前释放锁，
///*//        reentrantLock.lock();
        for (int i = 0; i < 3; i++) {
            new Thread(() -> {
            //lock实现抢票
                ReentrantLockUse(id);
            }, String.valueOf(i)+"P:A").start();
        }
//        reentrantLock.unlock();*/

        //synchronized实现线程交替执行加一减一,使用while防止虚假唤醒
//        alternate(id);

        //lock实现线程交替执行加一减一,使用while防止虚假唤醒
//        Alternate_Lock(id);

        //自定义线程的执行顺序
//        线程定制化化通信(id);
        //ArrayList集合不安全案例
//        ArrayListUnSafe(id);
        //Vector集合安全案例 jdk1.1
//        VectorDemo(id);
        //Collections集合安全案例 有点老了
//        CollectionsDemo(id);
        //CopyOnWriteArrayList 集合安全案例
//        copyOnWriteArrayList(id);
        //hashset线程不安全案例 使用
//        hashsetUnsafe(id);
//        copyOnWriteArraySet(id);
        //hashMap线程不安全案例
//        hashMapUnsafe(id);
//        concurrentHashMaps(id);
        //fork和join使用 计算靖江区技师今年的一些信息
//        forkAndJoin(id);
        //使用CompletableFuture:多维度分析计算一个比较耗时的节点
//        CompletableFutureUse(id);
        return "ok";
    }

    @SneakyThrows
    private void CompletableFutureUse(Long id) {
        CompletableFuture.supplyAsync(() -> {
            System.out.println("supplyAsync线程名:"+Thread.currentThread().getName());
            int nextInt = ThreadLocalRandom.current().nextInt(10);
//            try {Thread.sleep(5000);} catch (InterruptedException e) {throw new RuntimeException(e);}
            return String.valueOf(nextInt);

        }).whenComplete((r,e) ->{
            if(e == null) {
                System.out.println("whenComplete线程名:"+Thread.currentThread().getName());
                System.out.println("计算完成结果为：" + r);
            }
        }).exceptionally( throwable -> {
            System.out.println("出现异常："+throwable);
            return null;
        });
        System.out.println("线程名:"+Thread.currentThread().getName()+"：去忙去他的了");
    }

    @SneakyThrows
    private void forkAndJoin(Long id) {
        TiaolishiResult forkJoin = new TiaolishiResult();
        ArrayList<Integer> arrayList = new ArrayList<>();
        //查询锦江区的技师有1000个
        for (int i = 0; i < 1000; i++) {
            arrayList.add(i);
        }
        forkJoin.setUserId(arrayList);
        forkJoin.setStart(0);
        forkJoin.setEnd(arrayList.size());
        //使用fork和join切使用二分查找，
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        ForkJoinTask<Map<String, Tiaolishi>> submit = forkJoinPool.submit(forkJoin);
        Map<String, Tiaolishi> stringTiaolishiMap = submit.get();
        System.out.println("result:"+stringTiaolishiMap);
        forkJoinPool.shutdown();
    }

    private void concurrentHashMaps(Long id) {
        for (int i = 0; i < 30; i++) {
            int finalI = i;
            new Thread(() -> {
                concurrentHashMap.put(String.valueOf(finalI),String.valueOf(finalI));
                //读取时可能造成ConcurrentModificationException，因为多线程并发添加时在读取时可能在添加
                System.out.println(concurrentHashMap);
            }, String.valueOf(i)).start();
        }
    }

    private void copyOnWriteArraySet(Long id) {
        for (int i = 0; i < 30; i++) {
            int finalI = i;
            new Thread(() -> {
                copyOnWriteArraySet.add(String.valueOf(finalI));
                //读取时可能造成ConcurrentModificationException，因为多线程并发添加时在读取时可能在添加
                System.out.println(copyOnWriteArraySet);
            }, String.valueOf(i)).start();
        }
    }

    private void hashMapUnsafe(Long id) {
        HashMap<String, String> map = new HashMap<>();
        for (int i = 0; i < 30; i++) {
            int finalI = i;
            new Thread(() -> {
                map.put(String.valueOf(finalI),String.valueOf(finalI));
                //读取时可能造成ConcurrentModificationException，因为多线程并发添加时在读取时可能在添加
                System.out.println(map);
            }, String.valueOf(i)).start();
        }
    }

    private void hashsetUnsafe(Long id) {
        for (int i = 0; i < 30; i++) {
            int finalI = i;
            new Thread(() -> {
                set.add(String.valueOf(finalI));
                //读取时可能造成ConcurrentModificationException，因为多线程并发添加时在读取时可能在添加
                System.out.println(set);
            }, String.valueOf(i)).start();
        }
    }

    private void copyOnWriteArrayList(Long id) {
        for (int i = 0; i < 30; i++) {
            int finalI = i;
            new Thread(() -> {
                copyOnWriteArrayList.add(String.valueOf(finalI));
                //读取时可能造成ConcurrentModificationException，因为多线程并发添加时在读取时可能在添加
                System.out.println(copyOnWriteArrayList);
            }, String.valueOf(i)).start();
        }
    }

    private void CollectionsDemo(Long id) {
        for (int i = 0; i < 30; i++) {
            int finalI = i;
            new Thread(() -> {
                synchronizedList.add(String.valueOf(finalI));
                //读取时可能造成ConcurrentModificationException，因为多线程并发添加时在读取时可能在添加
                System.out.println(list);
            }, String.valueOf(i)).start();
        }
    }

    private void VectorDemo(Long id) {
        for (int i = 0; i < 30; i++) {
            int finalI = i;
            new Thread(() -> {
                vector.add(String.valueOf(finalI));
                //读取时可能造成ConcurrentModificationException，因为多线程并发添加时在读取时可能在添加
                System.out.println(list);
            }, String.valueOf(i)).start();
        }
    }

    /**
     * ArrayList集合不安全案例
     *
     * @param id
     */
    private void ArrayListUnSafe(Long id) {
        for (int i = 0; i < 30; i++) {
            int finalI = i;
            new Thread(() -> {
                list.add(String.valueOf(finalI));
                //读取时可能造成ConcurrentModificationException，因为多线程并发添加时在读取时可能在添加
                System.out.println(list);
            }, String.valueOf(i)).start();
        }
    }

    private void 线程定制化化通信(Long id) {
        new Thread(() -> {
            线程定制化化通信.printAA();
        }, "AA").start();
        new Thread(() -> {
            线程定制化化通信.printBB();
        }, "BB").start();
        new Thread(() -> {
            线程定制化化通信.printCC();
        }, "CC").start();
    }

    private void Alternate_Lock(Long id) {
        new Thread(() -> {
            alternate_lock.add();
        }, "A").start();
        new Thread(() -> {
            alternate_lock.decr();
        }, "B").start();
        new Thread(() -> {
            alternate_lock.add();
        }, "C").start();
        new Thread(() -> {
            alternate_lock.decr();
        }, "D").start();
    }

    private void alternate(Long id) {
        new Thread(() -> {
            alternate_synchronized.add();
        }, "A").start();
        new Thread(() -> {
            alternate_synchronized.decr();
        }, "B").start();
        new Thread(() -> {
            alternate_synchronized.add();
        }, "C").start();
        new Thread(() -> {
            alternate_synchronized.decr();
        }, "D").start();
    }

    @SneakyThrows
    private void ReentrantLockUse(Long id) {
        fairLock.lock();
        System.out.println(Thread.currentThread().getName() + "获取了锁");
        Thread.sleep(30000);
        //判断票数是否小于等于0
        /*if (Integer.valueOf(stringRedisTemplate.opsForValue().get("ticket")) <= 0L) {
            System.out.println("票卖完了" + id);
        } else {
            //判断该用户是否抢过
            if (stringRedisTemplate.opsForHash().hasKey("user", String.valueOf(id))) {
                System.out.println(id + "已经抢过了不能再抢了！");
            } else {
                //将抢到票的用户存入redis
                stringRedisTemplate.opsForHash().put("user", String.valueOf(id), "票号" + stringRedisTemplate.opsForValue().decrement("ticket"));
                System.out.println(Thread.currentThread().getName() + "抢到一张");
                //后续操作，使用中间件将抢到票的用户的票存入个人账户(数据库)，或则使用使用一个异步线程将票存入（数据库）
            }
        }*/
        fairLock.unlock();
        System.out.println(Thread.currentThread().getName() + "释放了锁");

    }

    private void SynchronizedUse(Long id) {
        //不能使用o当锁，想要保证线程安全，需保证synchronized锁的对象是同一个，比如可以使用 this, stringRedisTemplate,或者自己创建一个单例的对象
        Object o = new Object();
        //Singleton.getsingleton();
        new Thread(() -> {
            System.out.println(this.hashCode());
            System.out.println(DemoController.class.hashCode());
            synchronized (this) {
                //判断票数是否小于等于0
                if (Integer.valueOf(stringRedisTemplate.opsForValue().get("ticket")) <= 0L) {
                    System.out.println("票卖完了" + id);
                } else {
                    //判断该用户是否抢过
                    if (stringRedisTemplate.opsForHash().hasKey("user", String.valueOf(id))) {
                        System.out.println(id + "已经抢过了不能再抢了！");
                    } else {
                        //将抢到票的用户存入redis
                        stringRedisTemplate.opsForHash().put("user", String.valueOf(id), "票号" + stringRedisTemplate.opsForValue().decrement("ticket"));
                        System.out.println(Thread.currentThread().getName() + "抢到一张");
                        //后续操作，使用中间件将抢到票的用户的票存入个人账户(数据库)，或则使用使用一个异步线程将票存入（数据库）
                    }
                }
            }
        }, String.valueOf(id)).start();
    }

}
